import os

from hashlib import md5

from PyQt5.QtGui import QIntValidator
from xlsxwriter import Workbook

from PyQt5.QtCore import QDate, QSortFilterProxyModel, Qt, pyqtSignal, QRegExp
from PyQt5.QtWidgets import (QComboBox, QCompleter, QFileDialog, QMessageBox, QStyledItemDelegate, QStyleOptionComboBox,
                             QApplication, QStyle, QLineEdit, QListWidgetItem, QCheckBox, QListWidget)


def encrypt_pwd(password):
    """":return 32-length strings"""
    alg = md5()
    alg.update(bytes(password, encoding='utf-8'))
    return alg.hexdigest()


def export_model2xlsx(model, filename, parent=None):
    """model inhreit QSqlQueryModel or QTableModel etc.., has method record, rowCount, columnCount, headerData..."""
    r_cnt = model.rowCount()
    c_cnt = model.columnCount()
    if r_cnt > 0:
        directory = QFileDialog.getExistingDirectory(caption="选择存放的位置:")
        if directory:
            wk_path = os.path.join(directory, '{}.xlsx'.format(filename))
            wkb = Workbook(wk_path)
            sht = wkb.add_worksheet(filename)
            for r in range(r_cnt):
                for c in range(c_cnt):
                    if r == 0:
                        sht.write(r, c, model.headerData(c, Qt.Horizontal))
                    if hasattr(model, 'record'):
                        inner_val = model.record(r).value(c)
                    else:
                        inner_val = model.item(r, c).text()
                    if isinstance(inner_val, QDate):  # todo get string of obj in good implement
                        inner_val = inner_val.toString('yyyy/MM/dd')
                    sht.write(r + 1, c, inner_val)
            wkb.close()
            QMessageBox.information(parent, '完成', '文件地址：{}'.format(wk_path))


class IntRangeValidator(QIntValidator):
    """for QLineEdit validate for input range in bottom to top"""
    def __init__(self, bottom, top, line_edit):
        super().__init__(bottom, top)
        self.edit = line_edit

    def fixup(self, input: str):
        self.edit.setText("")


class MyComboBox(QComboBox):
    """usage: self.popupNew.connect(<handle_func>)"""
    popupNew = pyqtSignal()

    def __init__(self, parent=None):
        super(MyComboBox, self).__init__(parent)
        self.setEditable(True)
        self.filter_model = QSortFilterProxyModel(self)
        self.filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.filter_model.setSourceModel(self.model())
        self.completer = QCompleter(self.filter_model, self)
        self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        self.setCompleter(self.completer)
        self.lineEdit().textEdited.connect(self.filter_model.setFilterFixedString)
        self.lineEdit().setClearButtonEnabled(True)
        self.completer.activated.connect(self.on_completer_activated)

    def on_completer_activated(self, text):
        if text:
            index = self.findText(text)
            self.setCurrentIndex(index)
            self.activated[str].emit(self.itemText(index))

    def showPopup(self) -> None:
        curr_text = self.currentText()
        self.popupNew.emit()  # 自定义信号发射方法
        self.setCurrentText(curr_text)  # when popupNew, don't replace origin text
        return super(MyComboBox, self).showPopup()


class ComboBoxDelegate(QStyledItemDelegate):
    def __init__(self, parent, options) -> None:
        super(ComboBoxDelegate, self).__init__(parent=parent)
        self.options = options  # items for QComboBox

    def paint(self, painter, option, index):
        opt = QStyleOptionComboBox()
        opt.rect = option.rect
        opt.currentText = index.data()
        QApplication.style().drawComplexControl(QStyle.CC_Slider, opt, painter)
        QApplication.style().drawControl(QStyle.CE_ComboBoxLabel, opt, painter)

    def createEditor(self, parent, option, index):
        combobox = QComboBox(parent)
        combobox.addItems(self.options)
        return combobox


class ComboCheckBox(QComboBox):
    def __init__(self, parent=None):
        super(ComboCheckBox, self).__init__(parent)
        _line_edit = QLineEdit()
        _list_widget = QListWidget()
        self.setLineEdit(_line_edit)
        self.setModel(_list_widget.model())
        self.setView(_list_widget)
        select_all_box = QCheckBox('全选')
        select_all_box.stateChanged.connect(self.parse_box_select_all)
        self.box_list = [select_all_box]
        self.view().setItemWidget(QListWidgetItem(self.view()), select_all_box)

    def addItem(self, text):
        checkbox = QCheckBox(text)
        checkbox.stateChanged.connect(self.parse_box_select_single)
        self.box_list.append(checkbox)
        item = QListWidgetItem(self.view())
        self.view().setItemWidget(item, checkbox)

    def clear(self):
        while self.view().count() > 1:
            self.view().takeItem(1)
            self.box_list.pop()

    def parse_box_select_single(self) -> None:
        text = ''
        checked_boxes = []
        for box in self.box_list[1:]:
            if box.isChecked():
                checked_boxes.append(box.text())
        if checked_boxes:
            if len(checked_boxes) == len(self.box_list[1:]):
                self.box_list[0].setCheckState(2)
            else:
                self.box_list[0].setCheckState(1)
            text = ' '.join(checked_boxes)
        else:
            self.box_list[0].setCheckState(0)
        self.lineEdit().setText(text)

    def parse_box_select_all(self, state):
        if state == 0:
            for box in self.box_list:
                box.setCheckState(0)
        elif state == 2:
            for box in self.box_list:
                box.setCheckState(2)

    def get_all_checked_index(self):
        indexes = []
        for idx, box in enumerate(self.box_list[1:], 1):
            if box.isChecked():
                indexes.append(idx)
        return indexes

    def get_text_by_index(self, indexes):
        self.parse_box_select_all(0)
        ret = []
        for i in indexes:
            self.box_list[i].setCheckState(2)
            ret.append(self.box_list[i].text())
        return ' '.join(ret)


class MySortFilterProxyModel(QSortFilterProxyModel):
    def __init__(self):
        super(MySortFilterProxyModel, self).__init__()

    def lessThan(self, left, right):
        """自定义序列号按数字大小排序"""
        if not left.isValid() or not right.isValid():
            return False
        if left.column() == 1 and right.column() == 1:
            left_index = self.sourceModel().index(left.row(), 1)
            right_index = self.sourceModel().index(right.row(), 1)
            left_data = self.sourceModel().data(left_index)
            right_data = self.sourceModel().data(right_index)
            rx = QRegExp(r'\d+')
            rx.indexIn(left_data)
            left_data = rx.cap(0)
            rx.indexIn(right_data)
            right_data = rx.cap(0)
            int_left = int(left_data) if left_data else 0
            int_right = int(right_data) if right_data else 0
            return int_left < int_right
        else:
            return super().lessThan(left, right)
